﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Drawing;

using Emgu.CV;
using Emgu.CV.UI;
using Emgu.CV.Structure;

namespace MusicaTangible
{
    class BoardFromCam : Board
    {
        private Capture capture;
        private Image<Gray, Byte>[] shapeImageList;
        private Image<Gray, Byte>[] colorImageList;
        private Image<Gray, Byte> boardGrayImg;
        protected Image<Bgr, Byte> boardCapture;
        private Thread Capturator;

        public BoardFromCam(MusicProcessor parent, int numberOfColumns, int numberOfRows = 6)
        {
            this.NumberOfRows = numberOfRows;
            this.NumberofColumms = numberOfColumns;
            Initialize(numberOfRows, numberOfColumns);
            this.parent = parent;
            this.capture = new Capture(0);
            capture.SetCaptureProperty(Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FRAME_WIDTH, 1600);
            capture.SetCaptureProperty(Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT, 1200);
            this.Capturator = new Thread(this.CaptureThread);
            this.Capturator.Start();
            this.capture.SetCaptureProperty(Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_BRIGHTNESS, 1);

            shapeImageList = new Image<Gray, Byte>[Enum.GetNames(typeof(Shapes)).Length];
            colorImageList = new Image<Gray, Byte>[Enum.GetNames(typeof(Colors)).Length];

            this.loadTemplateImages();
        }

        public void addShapeImage(Shapes shape, string fileName)
        {
            this.shapeImageList[(int)shape] = new Image<Gray, Byte>(fileName);
        }

        public void addColorImage(Colors color, string fileName)
        {
            this.colorImageList[(int)color] = new Image<Gray, Byte>(fileName);
        }

        public void loadTemplateImages()
        {
            //addShapeImage(Shapes.circle, "Img\\Instrumentos\\circle.bmp");
            //addShapeImage(Shapes.square, "Img\\Instrumentos\\quad.bmp");
            //addShapeImage(Shapes.triangle, "Img\\Instrumentos\\triangle.bmp");
            //addShapeImage(Shapes.cross, "Img\\Instrumentos\\cross.bmp");
            //addShapeImage(Shapes.empty, "Img\\Instrumentos\\empty.bmp");

            addShapeImage(Shapes.circle, "Img\\Instrumentos\\1.bmp");
            addShapeImage(Shapes.square, "Img\\Instrumentos\\3.bmp");
            addShapeImage(Shapes.triangle, "Img\\Instrumentos\\2.bmp");
            addShapeImage(Shapes.cross, "Img\\Instrumentos\\4.bmp");
            addShapeImage(Shapes.empty, "Img\\Instrumentos\\empty.bmp");

            //System.Console.WriteLine("Cargue las imagenes de formas con exito");
           
            //addColorImage(Colors.blue, "Img\\Notas\\token_a.jpg");            
            //addColorImage(Colors.green, "Img\\Notas\\token_f.jpg");
            //addColorImage(Colors.lightblue, "Img\\Notas\\token_g.jpg");
            //addColorImage(Colors.orange, "Img\\Notas\\token_d.jpg");
            //addColorImage(Colors.purple, "Img\\Notas\\token_b.jpg");
            //addColorImage(Colors.red, "Img\\Notas\\token_c.jpg");
            //addColorImage(Colors.yellow, "Img\\Notas\\token_e.jpg");
            //addColorImage(Colors.empty, "Img\\Notas\\empty.bmp");

            addColorImage(Colors.blue, "Img\\Notas\\A.bmp");
            addColorImage(Colors.green, "Img\\Notas\\F.bmp");
            addColorImage(Colors.lightblue, "Img\\Notas\\G.bmp");
            addColorImage(Colors.orange, "Img\\Notas\\D.bmp");
            addColorImage(Colors.purple, "Img\\Notas\\B.bmp");
            addColorImage(Colors.red, "Img\\Notas\\C.bmp");
            addColorImage(Colors.yellow, "Img\\Notas\\E.bmp");
            addColorImage(Colors.empty, "Img\\Notas\\empty.bmp");

            //System.Console.WriteLine("Cargue las imagenes de colores con exito");            
        }        

        public override void Update()
        {
            this.captureImage();
            this.detectInstruments();
            this.detectNotes();
        }

        protected void CaptureThread()
        {
            while (true)
                boardCapture = this.capture.QueryFrame();
        }

        protected void captureImage()
        {
            //this.boardCapture.Save("antesDeCortar.jpg");

            Image<Bgr, byte> boardCapture2 = this.boardCapture;
            double scaleFactor = 880.0 / boardCapture2.Width; // 880 / 1280 = 0,6875
            Image<Bgr, Byte> resizedCapture = boardCapture2.Resize(scaleFactor, Emgu.CV.CvEnum.INTER.CV_INTER_AREA); // newPic = 880 x 660
            //Rectangle ROI = new Rectangle(0,0,850,535);
            //resizedCapture.Save("luegoDeResize.jpg");
            Rectangle ROI = new Rectangle(0, 0, 835, 545);
            boardGrayImg = resizedCapture.Copy(ROI).Convert<Gray, Byte>();

            //this.boardGrayImg.Save("postCorte.jpg");
        }

        protected void detectInstruments()
        {
            #region Initialization
            bool aux = false;

            int cantShapes = Enum.GetNames(typeof(Shapes)).Length; // Sí recorro el Empty
            int curY = 0;
            int height = this.boardGrayImg.Height;
            int step = height / 6;
            int width = this.boardGrayImg.Width / 9;
            int widthAux = this.boardGrayImg.Width;
            int heightAux = this.boardGrayImg.Height;
            #endregion
            #region Detection
            for (int i = 0; i < 6; i++)
            {
                aux = false;
                for (Shapes shape = 0; (int)shape < cantShapes; shape++)
                {
                    boardGrayImg.ROI = new Rectangle(0, curY, width, step);

                    boardGrayImg.Save("generated\\" + i + "-" + "0.png");
                    
                    Image<Gray, float> result = boardGrayImg.MatchTemplate(this.shapeImageList[(int)shape], Emgu.CV.CvEnum.TM_TYPE.CV_TM_CCORR_NORMED);
                    double[] min, max;
                    Point[] minLocations, maxLocations;
                    result.MinMax(out min, out max, out minLocations, out maxLocations);
                    int k = 0;
                    foreach (var point in maxLocations)
                        if (max[k++] > 0.97)
                        {
                            if (shape == Shapes.triangle)
                            {
                                result = boardGrayImg.MatchTemplate(shapeImageList[(int)Shapes.empty], Emgu.CV.CvEnum.TM_TYPE.CV_TM_CCORR_NORMED);

                                result.MinMax(out min, out max, out minLocations, out maxLocations);
                                k = 0;
                                foreach (var point2 in maxLocations)
                                    if (max[k++] > 0.999)
                                    {
                                        this.PlaceShapeOnBoard(i, Shapes.empty);
                                        aux = true;
                                    }
                                if (!aux)
                                {
                                    this.PlaceShapeOnBoard(i, Shapes.triangle);
                                    aux = true;
                                }
                            }
                            else
                            {
                                this.PlaceShapeOnBoard(i, shape);
                                aux = true;
                            }
                        }
                    if (aux)
                        break;

                }
                curY += step;
                if (curY > height) curY = height;
            }
            #endregion
            boardGrayImg.ROI = new Rectangle(0, 0, widthAux, heightAux);
        }

        protected void detectNotes()
        {
            #region Initialization
            bool aux = false;

            int cantColors = Enum.GetNames(typeof(Colors)).Length; // Sí recorro el Empty

            int height = this.boardGrayImg.Height;
            int width = this.boardGrayImg.Width;

            int widthAux = this.boardGrayImg.Width;
            int heightAux = this.boardGrayImg.Height;

            int stepX = width / 9;
            int stepY = height / 6;

            int curY = 0;
            int curX = stepX;
            #endregion
            #region Detection
            for (int i = 0; i < 6; i++) //rows
            {
                for (int j = 1; j < 9; j++) //columns
                {
                    aux = false;
                    for (Colors color = 0; (int)color < cantColors; color++)
                    {
                        boardGrayImg.ROI = new Rectangle(curX, curY, stepX, stepY);

                        boardGrayImg.Save("generated\\" + i + "-" + j + ".png");

                        Image<Gray, float> result;

                        try
                        {
                            result = boardGrayImg.MatchTemplate(this.colorImageList[(int)color], Emgu.CV.CvEnum.TM_TYPE.CV_TM_CCORR_NORMED);

                            double[] min, max;
                            Point[] minLocations, maxLocations;
                            result.MinMax(out min, out max, out minLocations, out maxLocations);
                            int k = 0;
                            foreach (var point in maxLocations)
                                if (max[k++] > 0.98)
                                {
                                    this.PlaceColorOnBoard(i, j, color);
                                    aux = true;
                                }
                        }
                        catch (Exception)
                        {
                            System.Console.WriteLine("OpenCV: Bad input roi in methos detectNotes from class BoardCam");
                        }    

                        if (aux)
                            break;
                    }
                    curX += stepX;
                    if (curX > width) curX = width;
                }
                curX = stepX;
                curY += stepY;
                if (curY > height) curY = height;
            }
            #endregion
            boardGrayImg.ROI = new Rectangle(0, 0, widthAux, heightAux);
        }

        public override void Stop()
        {
            this.Capturator.Abort();
        }
    }
}
